using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;

namespace SoftwareMonkeys.Scriptech.Projects
{
	public class ProjectAssemblyCSProjConverter
	{
		public ProjectAssembly ConvertFromFile(string csProjFilePath)
		{
			if (csProjFilePath == null)
				throw new ArgumentNullException("csProjFilePath");
			
			if (csProjFilePath == String.Empty)
				throw new ArgumentException("A *.csproj file path must be provided.", "csProjFilePath");
			
			string content = String.Empty;
			
			using (StreamReader reader = new StreamReader(File.OpenRead(csProjFilePath)))
			{
				content = reader.ReadToEnd();
			}
			
			return ConvertFromContent(csProjFilePath, content);
		}
		
		public ProjectAssembly ConvertFromContent(string csProjFilePath, string csProjFileContent)
		{
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(csProjFileContent);
			
			ProjectAssembly assembly = new ProjectAssembly();
			assembly.Name = GetAssemblyName(doc);
			assembly.BaseNamespace = GetBaseNamespace(doc);
			assembly.OutputFileName = GetAssemblyName(doc) + "." + GetAssemblyOutputExtension(doc);
			assembly.Directory = assembly.Name;
			assembly.ReferencedAssemblies = GetReferencedAssemblies(doc);
			assembly.ReferencedLibraries = GetReferencedLibraries(doc);
			
			return assembly;
		}
		
		public string GetAssemblyName(XmlDocument doc)
		{
			XmlNode nameNode = doc.SelectSingleNode("/p:Project/p:PropertyGroup/p:AssemblyName", CreateNamespaceManager(doc, "p"));
			
			string name = String.Empty;
			
			if (nameNode != null)
				name = nameNode.InnerText;
			
			return name;
		}
		
		public string GetBaseNamespace(XmlDocument doc)
		{			
			XmlNode node = doc.SelectSingleNode("/p:Project/p:PropertyGroup/p:RootNamespace", CreateNamespaceManager(doc, "p"));
			
			string baseNamespace = String.Empty;
			
			if (node != null)
				baseNamespace = node.InnerText;
			
			return baseNamespace;
		}
		
		public string GetAssemblyOutputExtension(XmlDocument doc)
		{
			XmlNode node = doc.SelectSingleNode("/p:Project/p:PropertyGroup/p:OutputType", CreateNamespaceManager(doc, "p"));
			
			string extension = String.Empty;
			string outputType = "Library";
			
			if (node != null)
				outputType = node.InnerText;
			
			return GetAssemblyOutputExtensionFromOutputType(outputType);
		}
		
		public string[] GetReferencedLibraries(XmlDocument doc)
		{
			XmlNodeList nodes = doc.SelectNodes("/p:Project/p:ItemGroup/p:Reference", CreateNamespaceManager(doc, "p"));
			
			List<string> libraries = new List<string>();
			
			foreach (XmlNode node in nodes)
			{
				string referencedLibrary = node.Attributes["Include"].Value;
			
				libraries.Add(referencedLibrary);
			}
			
			return libraries.ToArray();
		}
		
		
		public string[] GetReferencedAssemblies(XmlDocument doc)
		{
			XmlNodeList nodes = doc.SelectNodes("/p:Project/p:ItemGroup/p:ProjectReference", CreateNamespaceManager(doc, "p"));
			
			List<string> assemblyNames = new List<string>();
			
			foreach (XmlNode node in nodes)
			{
				XmlNode nameNode = node.SelectSingleNode("p:Name", CreateNamespaceManager(doc, "p"));
				
				if (nameNode == null)
					throw new Exception("Can't find name node.");
				
				string name = nameNode.InnerText;
				
				assemblyNames.Add(name);
			}
			
			return assemblyNames.ToArray();
		}
		
		public string GetAssemblyOutputExtensionFromOutputType(string outputType)
		{
			string outputExtension = String.Empty;
			
			switch (outputType)
			{
				case "Library":
					outputExtension = "dll";
					break;
				case "Exe":
					outputExtension = "exe";
					break;
				default:
					throw new ArgumentException("Output type not yet supported: " + outputType);
			}
			
			return outputExtension;
		}
		
		private XmlNamespaceManager CreateNamespaceManager(XmlDocument doc, string prefix)
		{
			XmlNamespaceManager xnManager = new XmlNamespaceManager(doc.NameTable);
			xnManager.AddNamespace(prefix, "http://schemas.microsoft.com/developer/msbuild/2003");
			
			return xnManager;
		}
		
		public string GetCSProjFilePath(string storeName, string groupName, string projectName, string assemblyName)
		{	
			string csProjFilePath = String.Empty;
		
			// Try getting the path from the expected location
			/*string assemblyDirectoryPath = ProjectAssemblyFileNamer.CreateAssemblyPath(storeName, groupName, projectName, assemblyDirectory, assemblyName);
		
			foreach (string file in Directory.GetFiles(assemblyDirectoryPath, "*.csproj", SearchOption.AllDirectories))
			{
				csProjFilePath = file;
			}	*/
			
			//if (csProjFilePath == String.Empty)
			//{
			string projectDirectoryPath = ProjectFileNamer.GetProjectPath(storeName, groupName, projectName);
		
			foreach (string filePath in Directory.GetFiles(projectDirectoryPath, "*.csproj", SearchOption.AllDirectories))
			{
				if (CSProjFileMatchesAssembly(filePath, groupName, projectName, assemblyName))
					csProjFilePath = filePath;
			}
			//}
			
			return csProjFilePath;
		}
		
		public bool CSProjFileMatchesAssembly(string filePath, string groupName, string projectName, string assemblyName)
		{
			string strippedFileName = Path.GetFileNameWithoutExtension(filePath).ToLower();
			strippedFileName = strippedFileName.Replace(projectName.ToLower() + ".", "");
			strippedFileName = strippedFileName.Replace(groupName.ToLower() + ".", "");
		
			string strippedAssemblyName = assemblyName.ToLower();
			strippedAssemblyName = strippedAssemblyName.Replace(projectName.ToLower() + ".", "");
			strippedAssemblyName = strippedAssemblyName.Replace(groupName.ToLower() + ".", "");
		
			return strippedFileName == strippedAssemblyName;
		}
	}
}